#include "camera.h"
 

 // 初始化帧缓冲设备
 static int fb_dev_init(void)
 {
     struct fb_var_screeninfo var = {0};  // 可变屏幕信息结构体，包含分辨率、颜色深度等信息
     struct fb_fix_screeninfo fix = {0};  // 固定屏幕信息结构体，包含显示内存的起始地址等硬件信息
     unsigned long screen_size;
 
     // 打开帧缓冲设备
     lcd_fd = open(FB_DEV, O_RDWR);  
     if (lcd_fd < 0) {
         fprintf(stderr, "打开帧缓冲设备失败: %s\n", FB_DEV);
         return -1;
     }
 
     // 获取可变和固定屏幕信息
     ioctl(lcd_fd, FBIOGET_VSCREENINFO, &var); // 获取屏幕的可变参数信息
     ioctl(lcd_fd, FBIOGET_FSCREENINFO, &fix); // 获取屏幕的固定参数信息
 
     screen_size = fix.line_length * var.yres; // 显存的总大小，line_length 是一行字节数，yres 是垂直分辨率
     width = var.xres; // 获取屏幕宽度
     height = var.yres; // 获取屏幕高度
 
     // 映射帧缓冲区内存
     screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);  
     if (screen_base == MAP_FAILED) {
         perror("内存映射失败");
         close(lcd_fd);
         return -1;
     }
     memset(screen_base, 0xFF, screen_size); // 将整个屏幕区域填充为白色
     return 0;
 }
 
 // 初始化视频捕获设备（V4L2）
 static int v4l2_dev_init(const char *device)
 {
     struct v4l2_capability cap = {0};  // 存储设备的能力信息 
     // /* █
     //    driver    : 驱动名称字符串（如uvcvideo） █
     //    card      : 设备名称字符串（如HD Camera） █
     //    bus_info  : 总线位置信息（如usb-0000:01:00.0-1） █
     //    version   : 驱动版本号（按KERNEL_VERSION宏编码） █
     //    capabilities : 设备能力位掩码 █
     //      V4L2_CAP_VIDEO_CAPTURE - 支持视频采集 █
     //      V4L2_CAP_STREAMING     - 支持流式IO █
     // */ █
     // 打开视频设备文件
     v4l2_fd = open(device, O_RDWR);  
     if (v4l2_fd < 0) {
         fprintf(stderr, "打开视频设备失败: %s\n", device);
         return -1;
     }
 
     // 查询设备的能力
     ioctl(v4l2_fd, VIDIOC_QUERYCAP, &cap);
//    成功返回0，失败返回-1 █
//    必须最先调用以验证设备能力 █ */ █
     // 检查设备是否支持视频捕获功能
     if (!(V4L2_CAP_VIDEO_CAPTURE & cap.capabilities)) {
         fprintf(stderr, "%s 不支持视频捕获功能\n", device);
         close(v4l2_fd);
         return -1;
     }
     return 0;
 }
 
 // 枚举并打印摄像头支持的视频格式
 static void v4l2_enum_formats(void)
 {
     struct v4l2_fmtdesc fmtdesc = {0};  // 用于描述视频格式的结构体
     fmtdesc.index = 0; // 格式索引从 0 开始
     fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 设置为视频捕获类型
 
     // 枚举摄像头支持的格式
     while (ioctl(v4l2_fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
         cam_fmt[fmtdesc.index].pixelformat = fmtdesc.pixelformat;  // 保存像素格式
         strcpy(cam_fmt[fmtdesc.index].description, fmtdesc.description);  // 保存格式描述
         fmtdesc.index++;
     }
 }
 
 // 打印摄像头支持的格式、分辨率、帧率
 static void v4l2_print_formats(void)
 {
     struct v4l2_frmsizeenum frmsize = {0};  // 用于枚举帧尺寸（宽度和高度）
     struct v4l2_frmivalenum frmival = {0};  // 用于枚举帧率（每秒帧数）
     int i;
 
     frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
     for (i = 0; cam_fmt[i].pixelformat && i < 10; i++) {  // 遍历所有支持的格式，防止越界
         printf("格式<0x%x>, 描述<%s>\n", cam_fmt[i].pixelformat, cam_fmt[i].description);
         frmsize.index = 0;
         frmsize.pixel_format = cam_fmt[i].pixelformat;
         frmival.pixel_format = cam_fmt[i].pixelformat;
 
         // 枚举当前格式下支持的分辨率
         while (ioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) {
             printf("分辨率<%d*%d> ", frmsize.discrete.width, frmsize.discrete.height);
             frmsize.index++;
             frmival.index = 0;
             frmival.width = frmsize.discrete.width;
             frmival.height = frmsize.discrete.height;
 
             // 枚举当前分辨率下支持的帧率
             while (ioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0) {
                 printf("<%dfps>", frmival.discrete.denominator / frmival.discrete.numerator);
                 frmival.index++;
             }
             printf("\n");
         }
         printf("\n");
     }
 }
 
 // 设置视频格式（分辨率、像素格式等）
 static void v4l2_set_format(void)
 {
     struct v4l2_format fmt = {0};  // 存储视频格式的结构体
     // /* █
//    type         : 必须为V4L2_BUF_TYPE_VIDEO_CAPTURE █
//    fmt.pix.width  : 请求宽度（驱动可能调整） █
//    fmt.pix.height : 请求高度 █
//    fmt.pix.pixelformat : 像素格式（V4L2_PIX_FMT_RGB565等） █
//    fmt.pix.bytesperline : 每行实际字节数（含填充） █
//    fmt.pix.sizeimage    : 单帧总字节数 █
     struct v4l2_streamparm parm = {0};  // 存储流参数的结构体
     //    type         : 必须为V4L2_BUF_TYPE_VIDEO_CAPTURE █
//    parm.capture.timeperframe : █
//       numerator   - 帧间隔分子（如1） █
//       denominator - 帧间隔分母（如30→30FPS） █
//    parm.capture.capability : █
//      V4L2_CAP_TIMEPERFRAME - 支持设置帧率 █
 
     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 设置视频捕获类型
     fmt.fmt.pix.width = width;  // 设置视频帧宽度为显示屏宽度
     fmt.fmt.pix.height = height;  // 设置视频帧高度为显示屏高度
     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;  // 设置像素格式为 RGB565
 
     // 使用 VIDIOC_S_FMT 设置视频格式
     if (ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt) < 0)
     //应用需检查返回的实际分辨率 █
//    可能被驱动调整为最接近的支持值 █ */ █
      {
         fprintf(stderr, "设置视频格式失败\n");
         return;
     }
 
     if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) {
         fprintf(stderr, "设置的像素格式不匹配\n");
         return;
     }
 
     frm_width = fmt.fmt.pix.width;  // 获取实际的视频宽度
     frm_height = fmt.fmt.pix.height;  // 获取实际的视频高度
     printf("视频帧大小<%d * %d>\n", frm_width, frm_height);
 
     parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     ioctl(v4l2_fd, VIDIOC_G_PARM, &parm);
 
     // 设置帧率（如设备支持）
     if (V4L2_CAP_TIMEPERFRAME & parm.parm.capture.capability) {
         parm.parm.capture.timeperframe.numerator = 1;    // 帧率的分子（表示 1 帧）
         parm.parm.capture.timeperframe.denominator = 30; // 帧率的分母（表示每秒 30 帧）
         if (ioctl(v4l2_fd, VIDIOC_S_PARM, &parm) < 0) {
             fprintf(stderr, "设置帧率失败\n");
             return;
         }
     }
 }
 
 // 初始化视频缓冲区（分配内存）
 static int v4l2_init_buffer(void)
 {
     struct v4l2_requestbuffers req = {0};  // 请求缓冲区的结构体
     // /* █
//    count    : 请求/实际分配的缓冲区数 █
//    type     : 必须与后续操作类型一致 █
//    memory   : 内存模式（V4L2_MEMORY_MMAP/USERPTR） █
// */ █
     struct v4l2_buffer buf = {0};  // 缓冲区的结构体
 
     req.count = FRAMEBUFFER_COUNT;  // 请求 3 个缓冲区
     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 设置为视频捕获类型
     req.memory = V4L2_MEMORY_MMAP;  // 使用内存映射的方式
 
     // 向驱动请求缓冲区
     if (ioctl(v4l2_fd, VIDIOC_REQBUFS, &req) < 0) {
         fprintf(stderr, "请求缓冲区失败\n");
         return -1;
     }
 
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
 
     // 查询缓冲区信息，并进行内存映射
     for (buf.index = 0; buf.index < FRAMEBUFFER_COUNT; buf.index++) {
         ioctl(v4l2_fd, VIDIOC_QUERYBUF, &buf);  // 查询缓冲区
         buf_infos[buf.index].length = buf.length;  // 存储缓冲区长度
         buf_infos[buf.index].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, v4l2_fd, buf.m.offset);  // 映射内存
         if (buf_infos[buf.index].start == MAP_FAILED) {
             fprintf(stderr, "内存映射失败\n");
             return -1;
         }
     }
 
     // 将缓冲区加入队列
     for (buf.index = 0; buf.index < FRAMEBUFFER_COUNT; buf.index++) {
         if (ioctl(v4l2_fd, VIDIOC_QBUF, &buf) < 0) {
             fprintf(stderr, "入队缓冲区失败\n");
             return -1;
         }
     }
     return 0;
 }
 
 // 启动视频流
 static int v4l2_stream_on(void)
 {
     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 视频捕获类型
     // 启动视频流
     if (ioctl(v4l2_fd, VIDIOC_STREAMON, &type) < 0) {
         fprintf(stderr, "启动视频流失败\n");
         return -1;
     }
     return 0;
 }
 static int v4l2_stream_off(void)
 {
     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 视频捕获类型
     // 启动视频流
     if (ioctl (v4l2_fd, VIDIOC_STREAMOFF, &type)<0) {
         fprintf(stderr, "关闭视频流失败\n");
         return -1;
     }
     return 0;
 }
 // 从视频设备读取数据并显示到屏幕
 static void v4l2_read_data(void)
 {
     struct v4l2_buffer buf = {0};  // 缓冲区结构体
     unsigned short *base;  // 帧缓冲的基地址
     unsigned short *start;  // 视频缓冲区的起始地址
     int min_w, min_h;
     int j;
 
     // 选择最小的宽度和高度，防止溢出
     min_w = (width > frm_width) ? frm_width : width;
     min_h = (height > frm_height) ? frm_height : height;
 
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
 
     // 无限循环读取视频数据并显示到屏幕
     for (;;) {
         for (buf.index = 0; buf.index < FRAMEBUFFER_COUNT; buf.index++) {
             ioctl(v4l2_fd, VIDIOC_DQBUF, &buf);  // 从队列中取出缓冲区
             for (j = 0, base = screen_base, start = buf_infos[buf.index].start; j < min_h; j++) {
                 memcpy(base, start, min_w * 2);  // 将视频数据复制到帧缓冲区
                 base += width;
                 start += frm_width;
             }
             ioctl(v4l2_fd, VIDIOC_QBUF, &buf);  // 将缓冲区放回队列
         }
     }
 }
 
void camera_init(void)
{
    // 初始化帧缓冲设备
     if (fb_dev_init()) 
         exit(EXIT_FAILURE); 
 
     // 初始化视频捕获设备
     if (v4l2_dev_init(ADDRESS_VIDEO) )
         exit(EXIT_FAILURE); 
     // 设置视频捕获格式
     if (v4l2_init_buffer()) 
     exit(EXIT_FAILURE);
 
     // 初始化视频缓冲区
     if (v4l2_init_buffer()) 
         exit(EXIT_FAILURE); 
}
void camera_use(int argc, char *argv[])
{
    if (argc != 2) { 
         fprintf(stderr, "用法: %s <视频设备>\n", argv[0]); 
         exit(EXIT_FAILURE); 
     } 
     camera_init();

     #ifdef carmera_test_show
        // 枚举并打印摄像头支持的格式、分辨率和帧率
     v4l2_enum_formats(); 
     v4l2_print_formats(); 
     #endif

     if(argv[1]==1)
     {
        if (v4l2_stream_on()) 
         exit(EXIT_FAILURE); 
     }else if(argv[1]==0)
     {
        if (v4l2_stream_off()) 
         exit(EXIT_FAILURE); 
     }else{
        printf("camera argv error");
        return -1;
     }

     #ifdef carmera_test_show
       // 循环读取数据并显示到屏幕
     v4l2_read_data();  
     #endif

     //传递数据
     exit(EXIT_SUCCESS); 

}








